using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Imagination.Physics;
using Imagination.Partitioning;
using Imagination.Collisions;
using Imagination.Display;
using Imagination.Diagnostics;

namespace Imagination
{
    public class ImaginationEngine : DrawableGameComponent, IContainThings
    {
        private Dictionary<int,Thing> things;
        private Game game;

        private PhysicsService physics;
        private Partitioner partitioner;
        private CollisionDetector detector;
        private ScreenManager screenManager;

        private DebugScreen debugScreen;

        private int frames;

        public ImaginationEngine(Game game) : base(game)
        {
            things = new Dictionary<int,Thing>();
            this.game = Game;
            game.Components.Add(this);
            game.Services.AddService(typeof(ImaginationEngine), this);
        }

        public int ThingCount
        {
            get { return things.Count; }
        }

        public PhysicsService Physics
        {
            get { return physics; }
        }
        public ScreenManager ScreenManager
        {
            get { return screenManager; }
        }

        public Partitioner Partitioner
        {
            get { return partitioner; }
        }

        public CollisionDetector CollisionDetector
        {
            get { return detector; }
        }

        public override void Initialize()
        {
            //  check for physics service
            RefreshServiceReferences();
            base.Initialize();
        }

        public void ToggleDebugScreen()
        {
            if (debugScreen == null)
            {
                debugScreen = new DebugScreen(this, game);
                debugScreen.Initialize();
                screenManager.ActiveScreens.Push(debugScreen);
            }
            else
            {
                debugScreen.IsActive = !debugScreen.IsActive;
            }

        }
        //public override void Draw(GameTime gameTime)
        //{
        //    screenManager.Draw(gameTime);
        //}
        public override void Update(GameTime gameTime)
        {
            frames++;
            base.Update(gameTime);
        }
        
        public void UpdateThings(GameTime gameTime)
        {
            // HACK: this method 

            //  calculate physics
            physics.BeginPhysics();
            foreach (Thing thing in things.Values)
                physics.Calculate(gameTime, thing);

            //  collision detection/response
            detector.BeginDetection();
            partitioner.CheckCollisions(detector);

            //  give Things a chance to think
            foreach (Thing thing in things.Values)
                thing.Update(gameTime);
        }

        public void RefreshServiceReferences()
        {
            physics = game.Services.GetService(typeof(PhysicsService)) as PhysicsService;
            if (physics == null)
            {
                //  add default physics service
                physics = new RigidBodyPhysicsService(this.game);
                game.Services.AddService(typeof(PhysicsService), physics);
            }

            //  check for partitioning service
            partitioner = game.Services.GetService(typeof(Partitioner)) as Partitioner;
            if (partitioner == null)
            {
                //  add default partitioner service
                partitioner = new SimplePartitioner();
                game.Services.AddService(typeof(Partitioner), partitioner);
            }

            //  check for collision detector
            detector = game.Services.GetService(typeof(CollisionDetector)) as CollisionDetector;
            if (detector == null)
            {
                detector = new SimpleDetector();
                game.Services.AddService(typeof(CollisionDetector), detector);
            }

            screenManager = game.Services.GetService(typeof(ScreenManager)) as ScreenManager;
            if (screenManager == null)
            {
                screenManager = new ScreenManager(game);
                game.Components.Add(screenManager);
                game.Services.AddService(typeof(ScreenManager), screenManager);
            }
        }

        #region IContainThings Members

        public void AddThing(Thing thing)
        {
            things.Add(thing.ID,thing);
            thing.Engine = this;
            partitioner.CheckPartitioning(thing);
            physics.InitializePhysics(thing);
        }

        public void RemoveThing(Thing thing)
        {
            things.Remove(thing.ID);
            //  HACK: probably better if the partitioner handled removing it from the zone
            thing.Zone.RemoveThing(thing);
            thing.Zone = null;
        }

        public void ClearThings()
        {
            things.Clear();
            partitioner.Clear();
        }

        public IEnumerable<Thing> EnumerateThings()
        {
            foreach (Thing thing in things.Values)
                yield return thing;
        }

        #endregion

        public Thing GetThing(int thingID)
        {
            if (!things.ContainsKey(thingID)) return null;
            return things[thingID];
        }
        
    }
}
